#include "tffmpegaudioplaythread.h"
#include <QDebug>
#include <QElapsedTimer>

TFFmpegAudioPlayThread::TFFmpegAudioPlayThread(QObject *parent) : QThread(parent)
{

}

TFFmpegAudioPlayThread::~TFFmpegAudioPlayThread()
{

}

/*
 * 开始播放
 */
void TFFmpegAudioPlayThread::startPlay()
{
    if(!isRunning()) {
        m_stopped = false;
        start();
    }
}

void TFFmpegAudioPlayThread::stopPlay()
{
    if(isRunning()) {
        m_stopped = true;
        quit();
        wait();
    }
}

/*
 * 初始化音频
 */
bool TFFmpegAudioPlayThread::initAudio()
{
    int ret;
    //查找音频流
    m_ffcustomPlayer.audio_stream_idx = av_find_best_stream(m_ffcustomPlayer.format_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,nullptr,0);
    if(m_ffcustomPlayer.audio_stream_idx < 0) {
        qDebug() << "find audio stream index error";
    }
    else {
        AVStream *audiostream = m_ffcustomPlayer.format_ctx->streams[m_ffcustomPlayer.audio_stream_idx];
        //打开解码器
        m_audioCtx = avcodec_alloc_context3(nullptr);
        ret = avcodec_parameters_to_context(m_audioCtx,audiostream->codecpar);
        if(ret < 0) {
            qDebug() << "fail avcodec context";
            return false;
        }
        m_audioCodec = avcodec_find_decoder(m_audioCtx->codec_id);
        if(m_audioCodec == nullptr) {
            qDebug() << "audio codec not found";
            return false;
        }
        //打开音频解码器
        int nret = avcodec_open2(m_audioCtx,m_audioCodec,nullptr);
        if(nret < 0) {
            qDebug() << "open audio codec error";
            return false;
        }

        //用于播放的 音频采样率 采样大小(用于初始化声卡) 采样声道
        int nsamplerate = m_audioCtx->sample_rate;
        int nsamplesize = 2;
        int nchannelcnt = 2;
        initAudioDevice(nsamplerate,nsamplesize,nchannelcnt);

        //创建重采样Context
        int64_t nchnout = av_get_default_channel_layout(nchannelcnt);
        int64_t nchnnin = av_get_default_channel_layout(m_audioCtx->channels);
        m_audioSwrCtx = swr_alloc_set_opts(m_audioSwrCtx,nchnout,AV_SAMPLE_FMT_S16,nsamplerate,nchnnin,m_audioCtx->sample_fmt,nsamplerate,0,nullptr);
        nret = swr_init(m_audioSwrCtx);
        if(nret >= 0) {
            quint64 nbytesize = (192000 * 3) / 2;
            m_audioData = reinterpret_cast<uint8_t*>(av_malloc(size_t(nbytesize) * sizeof(uint8_t)));
            if(!m_audioData) {
                av_free(m_audioData);
                return false;
            }
        }
        //分配音频帧
        m_audioFrame = av_frame_alloc();
    }
    return true;
}

/*
 * 初始化音频设备
 * nsamplerate : 音频采样率
 * nsamplesize : 采样数量
 * nchncnt     : 通道数量
 */
void TFFmpegAudioPlayThread::initAudioDevice(int nsamplerate, int nsamplesize, int nchncnt)
{
    QAudioFormat naudioformat;
    QAudioDeviceInfo ndefaultdeviceinfo = QAudioDeviceInfo::defaultOutputDevice();
    naudioformat = ndefaultdeviceinfo.preferredFormat();
    naudioformat.setCodec("audio/pcm");
    naudioformat.setSampleRate(nsamplerate);
    naudioformat.setSampleSize(nsamplesize*8);
    naudioformat.setChannelCount(nchncnt);
    naudioformat.setSampleType(QAudioFormat::SignedInt);
    naudioformat.setByteOrder(QAudioFormat::LittleEndian);
    m_audioOutput = new QAudioOutput(ndefaultdeviceinfo,naudioformat);
    m_audioOutput->setBufferSize(655360);
    m_audioOutput->setVolume(1.0);
    m_audioDevice = m_audioOutput->start();
}

/*
 * 解码音频
 */
void TFFmpegAudioPlayThread::decodeAudio(AVPacket *npkt)
{
    int nframefinish = avcodec_send_packet(m_audioCtx,npkt);
    if(nframefinish >= 0) {
        nframefinish = avcodec_receive_frame(m_audioCtx,m_audioFrame);
        if(nframefinish >= 0) {
            int noutchn = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
            int noutsize = av_samples_get_buffer_size(nullptr,noutchn,m_audioFrame->nb_samples,AV_SAMPLE_FMT_S16,1);
            int nret = swr_convert(m_audioSwrCtx,&m_audioData,noutsize,const_cast<const uint8_t **>(m_audioFrame->data),m_audioFrame->nb_samples);
            if(nret >= 0) {
                m_audioDevice->write((char*)m_audioData,noutsize);
            }
        }
    }
}

/*
 * 解码音频(参考SSPLAYER)
 */
void TFFmpegAudioPlayThread::decodeAudio2(AVPacket *npkt)
{
    int nret = 0;
    TFFCustomPlayer &ffcustomplayer = TFFmpegSupporter::get()->getFFCustomPlayer();

    nret = avcodec_send_packet(m_audioCtx,npkt);
    if(nret < 0) {
        qDebug() << "send packet error";
        return;
    }
    nret = avcodec_receive_frame(m_audioCtx,m_audioFrame);
    if(nret < 0) {
        qDebug() << "receive frame error";
        return;
    }

    int noutchn = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
    int noutsize = av_samples_get_buffer_size(nullptr,noutchn,m_audioFrame->nb_samples,AV_SAMPLE_FMT_S16,1);
    nret = swr_convert(m_audioSwrCtx,&m_audioData,noutsize,const_cast<const uint8_t **>(m_audioFrame->data),m_audioFrame->nb_samples);
    if(nret >= 0) {
        m_audioDevice->write((char*)m_audioData,noutsize);
    }

    //计算pts
    double audio_pts = (m_audioFrame->pts == AV_NOPTS_VALUE) ? NAN : m_audioFrame->pts * av_q2d(m_timeBase);
    double naudio_clock = 0;
    if(!isnan(audio_pts)) {
        naudio_clock = audio_pts + (double)m_audioFrame->nb_samples / m_audioFrame->sample_rate;
    }
    ffcustomplayer.audio_clock = naudio_clock;
    ffcustomplayer.audio_pts = audio_pts;
    //qDebug() << "audio pts" << audio_pts << "秒" << "audio clock" << naudio_clock << "秒" << "delay" << naudio_clock - audio_pts << "秒";
}

/*
 * 设置解码开始时间
 * m_startTime : 解码开始时间
 */
void TFFmpegAudioPlayThread::setStartTime(int64_t nstarttime)
{
    m_startTime = nstarttime;
}

/*
 * 设置时间基准
 * ntimebase : 时间基准
 */
void TFFmpegAudioPlayThread::setTimeBase(AVRational ntimebase)
{
    m_timeBase = ntimebase;
}

/*
 * 运行
 */
void TFFmpegAudioPlayThread::run()
{
    while (!m_stopped) {
        if(!m_ffcustomPlayer.audiopktList.isEmpty()) {
            QElapsedTimer ncodectime;
            ncodectime.start();
            AVPacket *audiopkt = m_ffcustomPlayer.popAudioFront();
            TFFmpegSupporter::delayTime(m_timeBase,audiopkt,m_startTime);
            decodeAudio2(audiopkt);
        }
        else {
            QThread::msleep(100);
        }
    }
}
